﻿using System;

using System.Collections.Generic;
using Silvermoon.Visuals;
using System.Drawing;
using Silvermoon.Animations;
using Silvermoon.Controls.Interfaces;
using Silvermoon.Controls.Classes;
using Silvermoon.Controls.Base;
using Silvermoon.Utils;
using Silvermoon.Images;
using Silvermoon.OpenGL;
using Silvermoon.Core;

namespace Silvermoon.Controls
{

    /// <summary>
    /// Control to show a hierachical page header.
    /// see also <seealso cref="T:PageContainer"/>.
    /// </summary>
    public class PageHeader : NotifyEventControl
    {
        const int arrowAlpha = 100;
        const int maxTransitionValue = 1000;

        public const int DefaultHeight = 64;
        const float shrinkFactor = 0.43F;

        static FontSprite font = new FontSprite(new Font(FontFamily.GenericSerif, 12.6f, FontStyle.Regular));

        #region Fields

        private Color titleColor;
        private Color color;
        private NavigationMode transitionMode = NavigationMode.None;
        private ButtonBase transitionButton;
        private BackgroundVisual background;
        private StringVisual titleTextVisual;
        private StringVisual parentTextVisual;
        private ImageVisual arrowLeft;
        private StringVisual nextTitleText;
        private StringVisual transitionTextVisual;
        private List<Visual> visuals;
        private ControlCollection buttons;
        private int transitionState;
        private ButtonBase button;
        private ScaleTransform parentTransform;
        private ScaleTransform transitionTextTransform;
        private ScaleTransform titleTransform;
        private IPage page;
        private int left;

        #endregion
        #region ctor
        public PageHeader()
            : base()
        {
            float scale = 1.0f; // 1.4f;
            visuals = new List<Visual>();
            buttons = new ControlCollection(this);
            Size = new Size(480, DefaultHeight);
            background = new BackgroundVisual();
            titleTextVisual = new StringVisual() { ScaleFactor = scale, LineMode = LineMode.SingleLineEndElipsis, Alignment = Alignment.Center, LineAlignment = Alignment.Center, Shadowed = true, Font = font };
            parentTextVisual = new StringVisual() { ScaleFactor = scale, LineMode = LineMode.SingleLineEndElipsis, Alignment = Alignment.Center, LineAlignment = Alignment.Center, Shadowed = true, Font = font };
            nextTitleText = new StringVisual() { ScaleFactor = scale, Visible = false, LineMode = LineMode.SingleLineEndElipsis, Alignment = Alignment.Center, LineAlignment = Alignment.Center, Shadowed = true, Font = font };
            transitionTextVisual = new StringVisual() { ScaleFactor = scale, Visible = false, LineMode = LineMode.SingleLineEndElipsis, Alignment = Alignment.Center, LineAlignment = Alignment.Center, Shadowed = true, Font = font };

            parentTransform = new ScaleTransform { X = shrinkFactor, Y = shrinkFactor };
            parentTextVisual.Transformations = new Transformation[] { parentTransform };

            arrowLeft = new ImageVisual() { Image = SysImages.LeftImage };

            transitionTextTransform = new ScaleTransform { X = shrinkFactor, Y = shrinkFactor };
            transitionTextVisual.Transformations = new Transformation[] { transitionTextTransform };

            titleTransform = new ScaleTransform();
            titleTextVisual.Transformations = new Transformation[] { titleTransform };

            color = SysColors.HeaderColor;
            titleColor = SysColors.HeaderTextColor;
            visuals.Add(background);
            visuals.Add(arrowLeft);
            visuals.Add(transitionTextVisual);
            visuals.Add(nextTitleText);
            visuals.Add(parentTextVisual);
            visuals.Add(titleTextVisual);
            Visuals = visuals;

        }

        #endregion
        #region Props

        /// <summary>
        /// Gets the title for the grand parent title when in transition.
        /// </summary>
        protected string GrandParentTitle { get { return GetGrandparentTitle(); } }

        /// <summary>
        /// Gets or sets the color for the header text.
        /// </summary>
        public Color TitleColor { get { return titleColor; } set { if (titleColor != value) { titleColor = value; Invalidate(InvalidationFlag.TextColor); } } }

        /// <summary>
        /// Gets or sets the color for the header.
        /// </summary>
        public Color Color { get { return color; } set { if (color != value) { color = value; Invalidate(InvalidationFlag.Color); } } }

        /// <summary>
        /// Gets the page title.
        /// </summary>
        public string Title { get { return page != null ? page.Title : string.Empty; } }

        /// <summary>
        /// Gets the title of the parent page.
        /// </summary>
        public string ParentTitle { get { return GetParentTitle(); } }

        /// <summary>
        /// Gets the button on the right.
        /// </summary>
        public ButtonBase Button { get { return page != null ? page.Button : null; } }


        /// <summary>
        /// Gets the transition mode.
        /// </summary>
        protected NavigationMode TransitionMode { get { return transitionMode; } private set { transitionMode = value; } }

        public bool IsInTransition { get { return transitionMode != NavigationMode.None; } }

        /// <summary>
        /// Gets the current transition position which can be from 0 to 1000 where 0 is the beginning state and 1000 the final state.
        /// </summary>
        public int TransitionState { get { return transitionState; } set { if (transitionState != value) { transitionState = value; OnTransitionPosChanged(); } } }

        /// <summary>
        /// Gets or sets the page that is displayed or in transition to be displayed.
        /// </summary>
        public IPage Page { get { return page; } set { if (page != value) { Select(value); OnPageChanged(); } } }

        /// <summary>
        /// Gets the page that is in transition to become the current page.
        /// </summary>
        public IPage TransitionPage { get; private set; }

        #endregion
        #region Methods;

        protected virtual string GetParentTitle()
        {
            IPage parent = page != null ? page.ParentPage : null;
            return parent != null ? parent.Title : string.Empty;
        }

        protected virtual string GetGrandparentTitle()
        {
            if (IsInTransition && transitionMode == NavigationMode.GoForward) return TransitionPage != null && TransitionPage.ParentPage != null ? TransitionPage.ParentPage.Title : string.Empty;
            IPage parent = page != null ? page.ParentPage : null;
            if (parent != null && !IsInTransition) parent = parent.ParentPage;
            return parent != null ? parent.Title : string.Empty;
        }

        protected override void OnSizeChanged()
        {
            if (Height != DefaultHeight) Size = new Size(Size.Width, DefaultHeight);
            else base.OnSizeChanged();
        }

        protected virtual void OnTransitionPosChanged()
        {
            Invalidate(InvalidationFlag.LayoutAppearance);
        }

        protected override void OnLayout()
        {
            background.Size = Control.Scale(Size);

            if ((InvalidationMask & InvalidationFlag.Color) != 0)
            {
                background.Colors = ColorUtil.CreatePlasticColors(color);
            }

            background.Alpha = Alpha;

            left = Width / 4;
            this.parentTextVisual.Text = ParentTitle;
            this.titleTextVisual.Text = Title;
            transitionTextVisual.Text = GrandParentTitle;
            button = Button;
            arrowLeft.Bounds = Control.ScaledRect(left - 8, (Height - 16) / 2, 16, 16);
            AdjustButton();


            if (transitionMode == NavigationMode.None) AdjustNoAnimation(); else AdjustAnimation();

            base.OnLayout();
        }

        private void AdjustNoAnimation()
        {
            if (CheckFlag(InvalidationFlag.LayoutAppearance | InvalidationFlag.Bounds))
            {
                transitionTextVisual.Visible = false;

                transitionButton = null;
                this.parentTextVisual.Color = titleColor;
                parentTransform.Y = parentTransform.X = shrinkFactor;
                this.titleTextVisual.Color = titleColor;
                titleTransform.X = 1f;
                titleTransform.Y = 1f;
                arrowLeft.Visible = page != null && page.ParentPage != null;
                arrowLeft.Alpha = arrowAlpha;

                int h = (int)(parentTextVisual.Font.CharHeight * shrinkFactor + 0.5f);
                parentTextVisual.Bounds = Control.ScaledRect(6, (Height - h) / 2, Width / 2 - 12, h);
                h = (int)titleTextVisual.Font.CharHeight;
                titleTextVisual.Bounds = Control.ScaledRect(Width / 4 + 12, (Height - h) / 2, Width / 2 - 12, h);

            }
        }

        protected virtual void AdjustButton()
        {
            var oldBtn = buttons.Count > 0 ? (Button)buttons[0] : null;
            if (oldBtn != button || buttons.Count > 1)
            {
                buttons.Clear();
                if (button != null)
                {
                    buttons.Add(button);
                }
            }
            SetButtonBounds(button);
        }

        private void SetButtonBounds(ButtonBase button)
        {
            if (button != null)
            {
                int buttonWidth = Math.Min(116, button.Width);
                if (buttonWidth < 48) buttonWidth = 48;
                int buttonHeight = Math.Min(PageHeader.DefaultHeight - 6, button.Height);
                if (buttonHeight < 48) buttonHeight = 48;
                button.Bounds = new Rectangle(
                    Width - buttonWidth - 4,
                    (Height - buttonHeight) / 2,
                    buttonWidth,
                    buttonHeight);
            }
        }

        protected internal override IList<Control> SubControls
        {
            get { return buttons; }
        }


        public void GoBack()
        {
            if (page != null && page.ParentPage != null) Page = page.ParentPage;
        }


        private void Select(IPage page)
        {
            if (!Invalidated)
            {
                // don't start a new transition when already in another:
                if (page != this.page && !IsInTransition)
                {
                    if (this.page != null && page == this.page.ParentPage) TransitionMode = NavigationMode.GoBack;
                    else if (page != null && this.page == page.ParentPage) TransitionMode = NavigationMode.GoForward;
                    else TransitionMode = NavigationMode.Change;

                    TransitionPage = this.page;
                    this.page = page;
                    transitionState = 0;
                    OnTransition(0, PageTransitionState.Begin);
                    AnimEventArgs e = new AnimEventArgs(Animation.DefaultDuration, 0, maxTransitionValue, 0);
                    e.Progress += new EventHandler<AnimEventArgs>(ProgressTransition);
                    //Invalidate(InvalidationFlag.LayoutAppearance);
                    e.Invoke();
                }
            }
            else this.page = page;
        }

        protected virtual void ProgressTransition(object sender, AnimEventArgs e)
        {
            PageTransitionState state = PageTransitionState.Progress;
            int value = e.Value;
            //  if (value == e.BeginValue) state = PageTransitionState.Begin;
            if (e.IsCompleted) state = PageTransitionState.End;
            transitionState = value;
            OnTransition(value, state);
            if (e.IsCompleted) CompleteTransition();
            Invalidate(InvalidationFlag.LayoutAppearance | InvalidationFlag.Custom);
        }

        protected virtual void CompleteTransition()
        {
            TransitionMode = NavigationMode.None;
            TransitionPage = null;
        }



        /// <summary>
        /// Adjusts the appearance whilst in animation.
        /// </summary>
        protected virtual void AdjustAnimation()
        {
            if (CheckFlag(InvalidationFlag.LayoutAppearance))
            {
                int alpha = (255 * transitionState / maxTransitionValue);

                transitionButton = TransitionPage != null ? TransitionPage.Button : null;
                if (transitionMode != NavigationMode.Change) NavigateAnimation(alpha); else ChangeAnimation(alpha);
                AdjustBackArrow(alpha);
                AdjustButtons(alpha);
            }
        }

        private void ChangeAnimation(int alpha)
        {
            if (titleTextVisual.Text != parentTextVisual.Text)
            {
                transitionTextVisual.Text = TransitionPage != null ? TransitionPage.Title : string.Empty;
                transitionTextVisual.Visible = true;
                AdjustTitle(titleTextVisual, alpha, 0, titleTransform);
                AdjustTitle(transitionTextVisual, 255 - alpha, 0, transitionTextTransform);
            }
            else
            {
                AdjustTitle(titleTextVisual, 255, 0, titleTransform);
                transitionTextVisual.Visible = false;
            }

            string text = Page != null && Page.ParentPage != null ? Page.ParentPage.Title : null;
            if (text == null)
            {
                text = TransitionPage != null && TransitionPage.ParentPage != null ? TransitionPage.ParentPage.Title : string.Empty;
                alpha = 255 - alpha;
            }
            parentTextVisual.Text = text;
            AdjustParent(parentTextVisual, 0, parentTransform);
            parentTextVisual.Color = ColorUtil.AlphaColor(titleColor, alpha);
        }

        private void NavigateAnimation(int alpha)
        {
            int transitionLevel = this.transitionState;

            if (transitionMode == NavigationMode.GoForward) transitionLevel = maxTransitionValue - transitionLevel;

            SetButtonBounds(transitionButton);

            int a2 = alpha;
            alpha = 255 * transitionLevel / maxTransitionValue * 2;
            if (alpha > 255) alpha = 255;
            alpha = 255 - alpha;

            if (transitionMode == NavigationMode.GoForward)
            {
                AdjustGrandParent(a2, transitionLevel);
                AdjustParent(parentTextVisual, transitionLevel, parentTransform);
                AdjustTitle(titleTextVisual, alpha, transitionLevel, titleTransform);
            }
            else
            {
                AdjustGrandParent(255 - a2, transitionLevel);
                AdjustParent(titleTextVisual, transitionLevel, titleTransform);
                parentTextVisual.Text = TransitionPage.Title;
                AdjustTitle(parentTextVisual, alpha, transitionLevel, parentTransform);
            }

        }

        private void AdjustBackArrow(int alpha)
        {
            bool hasCurrent = page != null && page.ParentPage != null;
            bool hasParent = TransitionPage != null && TransitionPage.ParentPage != null;
            if (hasCurrent != hasParent)
            {
                int a = hasCurrent ? alpha : 255 - alpha;
                arrowLeft.Visible = true;
                arrowLeft.Alpha = arrowAlpha * a / 255;
            }
            else
            {
                arrowLeft.Alpha = hasCurrent ? arrowAlpha : 0;
                arrowLeft.Visible = true;
            }
        }

        private void AdjustTitle(StringVisual title, int alpha, int transitionLevel, ScaleTransform transform)
        {
            int h = title.Font.CharHeight;
            int newPos = (Width * transitionLevel / maxTransitionValue);
            title.Bounds = Control.ScaledRect(newPos + Width / 4 + 12, (Height - h) / 2, Width / 2 - 12, h);

            transform.X = 1f;
            transform.Y = 1f;
            title.Color = ColorUtil.AlphaColor(titleColor, alpha);
        }

        private void AdjustParent(StringVisual parent, int transitionLevel, ScaleTransform transform)
        {
            //int deltaPos = (Width / 2 - Width / 8) * transitionLevel / maxTransitionValue;
            int deltaPos = (Width / 4 + 12 - 6) * transitionLevel / maxTransitionValue;
            int h = (int)(parent.Font.CharHeight * shrinkFactor + 0.5f);
            parent.Bounds = Control.ScaledRect(deltaPos + 6, (Height - h) / 2, Width / 2 - 12, h);

            float deltaScale = 1.0F - shrinkFactor;
            float newScale = (deltaScale * transitionLevel / maxTransitionValue) + shrinkFactor;
            transform.X = newScale;
            transform.Y = newScale;
        }

        private void AdjustButtons(int alpha)
        {
            if (button != null)
            {
                button.Alpha = alpha;
                button.Visible = true;
            }
            if (transitionButton != null)
            {
                if (!buttons.Contains(transitionButton)) buttons.Add(transitionButton);
                transitionButton.Visible = true;
                transitionButton.Alpha = 255 - alpha;
            }
        }

        private void AdjustGrandParent(int alpha, int transitionLevel)
        {
            StringVisual parent = transitionTextVisual;
            int h = (int)(parent.Font.CharHeight * shrinkFactor + 0.5f);
            parent.Bounds = Control.ScaledRect(6, (Height - h) / 2, Width / 2 - 12, h);

            float scale = ((shrinkFactor - 0.01F) * transitionLevel / maxTransitionValue) + 0.01f;
            transitionTextTransform.X = transitionTextTransform.Y = scale;
            transitionTextVisual.Color = ColorUtil.AlphaColor(titleColor, 255 - alpha);
            transitionTextVisual.Visible = true;
        }

        Point p;

        public override void OnMouseDown(MouseDownEventArgs e)
        {
            p = e.ScreenToClient(e.Position);
            e.Handled = true;
            base.OnMouseDown(e);
        }

        public override void OnMouseUp(MouseUpEventArgs e)
        {
            if (!e.Handled) e.AllowDoubleClick = false;
            base.OnMouseUp(e);
        }

        public override void OnMouseClick(MouseClickEventArgs e)
        {
            if (!e.Handled)
            {
                Rectangle r = new Rectangle(0, 0, Width / 4, Height);
                e.Handled = r.Contains(p);
                if (e.Handled) GoBack();
            }
        }

        protected virtual void OnPageChanged()
        {
            if (PageChanged != null)
            {
                PageChanged(this, EventArgs.Empty);
            }
        }

        protected virtual void OnTransition(int progress, PageTransitionState mode)
        {
            if (Transition != null)
            {
                PageTransitionEventArgs e = new PageTransitionEventArgs();
                e.Mode = transitionMode;
                e.Page = this.Page;
                e.TransitionPage = this.TransitionPage;
                e.BeginValue = 0;
                e.EndValue = 1000;
                e.Progess = progress;
                e.State = mode;
                Transition(this, e);
            }
        }

        #endregion
        #region events

        public event EventHandler PageChanged;
        public event EventHandler<PageTransitionEventArgs> Transition;

        #endregion

    }
}
